package com.example.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * JWT工具类，用于生成jwt令牌
 *
 * @auth shanyue
 * @date 2025年06月16日9:03
 */
@Component
public class JwtUtil {

    //签名密钥，从配置文件中注入
    @Value("${spring.security.jwt.key}")
    String key;

    //令牌过期时间，从配置文件中注入
    @Value("${spring.security.jwt.expire}")
    int expire;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 创建JWT令牌
     *
     * @param details  当前用户的信息
     * @param id       用户ID
     * @param username 用户名
     * @return 生成的JWT字符串
     */
    public String createJwt(UserDetails details, int id, String username) {
        Algorithm algorithm = Algorithm.HMAC256(key);
        Date expire = expireDate();
        return JWT.create()
                .withJWTId(UUID.randomUUID().toString())
                .withClaim("id", id)
                .withClaim("name", username)
                .withClaim("authorities", details.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                .withExpiresAt(expire)
                .withIssuedAt(new Date())
                .sign(algorithm);
    }

    /**
     * 解析JWT令牌
     *
     * @param headerToken 请求头中的令牌 Bearer token
     * @return 解析成功返回DecodedJWT对象，失败或过期返回null
     */
    public DecodedJWT resolveJwt(String headerToken) {
        String token = this.convertToken(headerToken);
        if (token == null) {
            return null;
        }
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT verify = jwtVerifier.verify(token);
            if (this.isInvalidToken(verify.getId())) {
                return null;
            }
            Date expiresAt = verify.getExpiresAt();
            return new Date().after(expiresAt) ? null : verify;
        } catch (JWTVerificationException e) {
            return null;
        }
    }

    /**
     * 计算JWT的过期时间
     *
     * @return 当前时间加上配置后的天数
     */
    public Date expireDate() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR, expire * 24);
        return calendar.getTime();
    }

    /**
     * 从DecodedJWT对象中还原出SpringSecurity的UserDetails对象
     *
     * @param jwt 已解析的JWT对象
     * @return 构造的UserDetails对象
     */
    public UserDetails toUser(DecodedJWT jwt) {
        Map<String, Claim> claims = jwt.getClaims();
        return User
                .withUsername(claims.get("name").asString())
                .password("123456")
                .authorities(claims.get("authorities").asArray(String.class))
                .build();
    }

    /**
     * 从DecodedJWT中提取用户ID
     *
     * @param jwt 已解析的JWT对象
     * @return 用户ID
     */
    public Integer toId(DecodedJWT jwt) {
        Map<String, Claim> claims = jwt.getClaims();
        return claims.get("id").asInt();
    }

    /**
     * 将令牌加入黑名单(用于登出、强制失效处理)
     * @param headerToken   请求头中的令牌 Bearer token
     * @return  true表示加入黑名单成功，false表示失败
     */
    public boolean invalidateJwt(String headerToken) {
        String token = this.convertToken(headerToken);
        if (token == null) {
            return false;
        }
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT jwt = jwtVerifier.verify(token);
            //获取唯一标识
            String id = jwt.getId();
            //将ID加入Redis黑名单
            return deleteToken(id, jwt.getExpiresAt());
        } catch (JWTVerificationException e) {
            return false;
        }
    }

    /**
     * 从Authorization头部提取Token字符串
     *
     * @param headerToken 请求头中的令牌 Bearer token
     * @return 提取的token字符串，为空或格式错误返回null
     */
    private String convertToken(String headerToken) {
        if (headerToken == null || !headerToken.startsWith("Bearer")) {
            return null;
        }
        return headerToken.substring(7);
    }

    /**
     * 将指定JWT ID加入Redis黑名单
     * @param uuid  JWT的唯一ID
     * @param time  JWT的过期时间
     * @return  加入成功返回true，否则false
     */
    private boolean deleteToken(String uuid, Date time) {
        if (this.isInvalidToken(uuid)) {
            //已在黑名单中
            return false;
        }
        Date now = new Date();
        long expire = Math.max(time.getTime() - now.getTime(), 0);
        //使用Redis存储空值作为标记，过期时间为JWT剩余时间
        stringRedisTemplate.opsForValue().set(Const.JWT_BLACK_LIST + uuid, "", expire, TimeUnit.MILLISECONDS);
        return true;
    }

    /**
     * 判断指定JWT是否已经作废(在黑名单中)
     * @param uuid  JWT的唯一ID
     * @return  true表示作废，false表示有效
     */
    private boolean isInvalidToken(String uuid) {
        return Boolean.TRUE.equals(stringRedisTemplate.hasKey(Const.JWT_BLACK_LIST + uuid));
    }

}
